package com.easy.module.system.service.impl;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.easy.framework.common.constant.Constants;
import com.easy.framework.common.exception.ServiceException;
import com.easy.framework.common.utils.AssertUtil;
import com.easy.framework.common.validate.RegexValidator;
import com.easy.module.system.entity.SysTemplate;
import com.easy.module.system.service.IEmailCaptchaService;
import com.easy.module.system.service.IEmailService;
import com.easy.module.system.service.ISysConfService;
import com.easy.module.system.service.ISysTemplateService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
@Service
public class EmailCaptchaServiceImpl implements IEmailCaptchaService {

    private final StringRedisTemplate stringRedisTemplate;

    private final ISysConfService sysConfService;

    private final ISysTemplateService sysTemplateService;

    // 允许的最大验证次数
    private final int MAX_VERIFY_ATTEMPTS = 6;

    private final String VERIFY_ATTEMPT_PREFIX = Constants.CACHE + "email:verify:attempt:";

    private final String EMAIL_CODE = Constants.CACHE + "email:code:";

    private final String SYS_NOTIFY_TYPE_KEY = "sys.notify.type";

    @Override
    public String sendCaptcha(final String to, final SysTemplate template, final String code, final Map<String, Object> variables) {
        AssertUtil.isTrue(RegexValidator.isValidEmail(to), "不是系统支持的邮箱地址");
        AssertUtil.isNotNull(template, "模板不存在");
        String content = sysTemplateService.generateTemplate(template.getContent(), variables);
        String type = sysConfService.getValue(SYS_NOTIFY_TYPE_KEY);
        Map<String, IEmailService> emailServiceMap = SpringUtil
                .getBeansOfType(IEmailService.class);
        Optional<IEmailService> optional = emailServiceMap.values()
                .stream()
                .filter(service -> service.support(type))
                .findFirst();
        String ck = String.format("%s:%s", to, template.getCode());
        optional.get().send(to, template.getName(), content);
        stringRedisTemplate.opsForValue().set(ck, code, 5, TimeUnit.MINUTES);
        return HexUtil.encodeHexStr(ck, CharsetUtil.CHARSET_UTF_8);
    }

    @Override
    public String sendCaptcha(final String to, final String templateCode, final String code, final Map<String, Object> variables) {
        AssertUtil.isNotEmpty(templateCode, "模板不存在");
        SysTemplate sysTemplate = sysTemplateService.getByTemplateCode(templateCode);
        return sendCaptcha(to, sysTemplate, code, variables);
    }

    /**
     * 验证验证码
     *
     * @param captchaKey
     * @param code
     */
    @Override
    public void verifyCode(String captchaKey, String code) {
        String ck = HexUtil.decodeHexStr(captchaKey);
        int verifyAttempts = getVerifyAttempts(ck);
        if (verifyAttempts >= MAX_VERIFY_ATTEMPTS) {
            throw new ServiceException("验证次数过多，请重新获取邮箱后再试");
        }
        // 邮箱验证码验证逻辑
        boolean isValid = validateEmailCode(ck, code);
        if (!isValid) {
            stringRedisTemplate.opsForValue().increment(getVerifyAttemptKey(ck), 1); // 增加验证次数
            throw new ServiceException("邮箱验证码错误");
        }
        clearVerifyAttempts(ck);
    }

    private void clearVerifyAttempts(String businessKey) {
        String verifyAttemptKey = getVerifyAttemptKey(businessKey);
        stringRedisTemplate.delete(verifyAttemptKey);
    }

    private int getVerifyAttempts(String businessKey) {
        String verifyAttemptKey = getVerifyAttemptKey(businessKey);
        String attempts = stringRedisTemplate.opsForValue().get(verifyAttemptKey);
        return attempts != null ? Integer.parseInt(attempts) : 0;
    }

    private String getVerifyAttemptKey(String businessKey) {
        return VERIFY_ATTEMPT_PREFIX + businessKey;
    }

    private boolean validateEmailCode(String businessKey, String code) {
        String codeStr = stringRedisTemplate.opsForValue().get(EMAIL_CODE + businessKey);
        return code.equals(codeStr);
    }
}
